function cloneShallow (source) {
  var target = {};
  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = source[key]
    }
  }
  return target;
}
cloneShallow();


// 深拷贝1
function cloneDeep1 (source) {
  var target = {};
  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (typeof source[key] === 'object') {
        target[key] = cloneDeep1(source[key])
      } else {
        target[key] = source[key]
      }
    }
  }
}
cloneDeep1()

// 循环引用

function isObject (obj) {
  return typeof obj === 'object' ? obj : null
}


function cloneDeep3(source, hash = new WeakMap()) {
  if (!isObject(source)) return source;
  if (hash.has(source)) return hash.get(source);

  var target = Array.isArray(source) ? [] : {};

  for (let key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (isObject(source[key])) {
        target[key] = cloneDeep3(source[key], hash)
      } else {
        target[key] = source[key]
      }
    }
  }
  return target
}

cloneDeep3()


// kuaipai

function quickSort (arr) {
  if (arr.length <= 1) return arr;
  let pivot = arr.length / 2 | 0;
  let pivotValue = arr.splice(pivot, 1)[0];

  let leftArr = [];
  let rightArr = [];
  arr.forEach((val) => {
    val > pivotValue ? rightArr.push(val) : leftArr.push(val)
  })

  return [...quickSort(leftArr), pivotValue, ...quickSort(rightArr)]
}

// 防抖
function debounce (fn, delay) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

// Promise all
Promise.all = function(promises) {
  const values = [];
  let count = 0;
  return new Promise((resolve) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then((res) => {
        count++;
        values[index] = res;
        if (count === promises.length) {
          resolve(values)
        }
      }, err => {
        console.log(err)
      })
    })
  })
}


// Promsie.allSeleted 

Promise.allSeleted = function (promises) {
  let count = 0;
  let result = [];
  return new Promise((resolve) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then((res) => {
        result[index] = {
          value: res,
          reason: null
        }
      }, err => {
        result[index] = {
          value: null,
          reason: err
        }
      })
    }).finally(() => {
      count++
      if (count === promises.length) {
        resolve(result)
      }
    })
  })
}

// 实现maxRequest，成功后resolve结果，失败后重试，尝试超过一定次数才真正的reject
function maxRequest(fn, maxNum) {
  return new Promise((resolve, reject) => {
    function help(index) {
      Promise.resolve(fn()).then((res) => {
        resolve(res)
      }).catch(err => {
        if (index - 1 > 0) {
          help(maxNum);
        } else {
          reject(err)
        }
      })
    }
    help(maxNum)
  })
}

// 单列模式

class SingletonLogin {
  constructor(name, password) {
    this.name = name
    this.password = password
  }
  static getInstance(name, password) {
    if (this.instance) this.instance = new SingletonLogin(name, password);
    return this.instance;
  }
}

//

// 我了解的 js 中实现继承的几种方式有：

// （1）第一种是以原型链的方式来实现继承，但是这种实现方式存在的缺点是，在包含有引用类型的数据时，
// 会被所有的实例对象所共享，容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。

// （2）第二种方式是使用借用构造函数的方式，这种方式是通过在子类型的函数中调用超类型的构造函数来实现的，
// 这一种方法解决了不能向超类型传递参数的缺点，但是它存在的一个问题就是无法实现函数方法的复用，并且超类型原型定义的方法子类型也没有办法访问到。

// （3）第三种方式是组合继承，组合继承是将原型链和借用构造函数组合起来使用的一种方式。
// 通过借用构造函数的方式来实现类型的属性的继承，通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题，但是由于我们是以超类型的实例来作为子类型的原型，所以调用了两次超类的构造函数，造成了子类型的原型中多了很多不必要的属性。

// （4）第四种方式是原型式继承，原型式继承的主要思路就是基于已有的对象来创建新的对象，
// 实现的原理是，向函数中传入一个对象，然后返回一个以这个对象为原型的对象。
// 这种继承的思路主要不是为了实现创造一种新的类型，只是对某个对象实现一种简单继承，
// ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。

// （5）第五种方式是寄生式继承，寄生式继承的思路是创建一个用于封装继承过程的函数，
// 通过传入一个对象，然后复制一个对象的副本，然后对象进行扩展，最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承，如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。

// （6）第六种方式是寄生式组合继承，组合继承的缺点就是使用超类型的实例做为子类型的原型
// ，导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型，这样就避免了创建不必要的属性。


// 我们一般使用字面量的形式直接创建对象，但是这种创建方式对于创建大量相似对象的时候，会产生大量的重复代码。但 js
// 和一般的面向对象的语言不同，在 ES6 之前它没有类的概念。但是我们可以使用函数来进行模拟，从而产生出可复用的对象
// 创建方式，我了解到的方式有这么几种：

// （1）第一种是工厂模式，工厂模式的主要工作原理是用函数来封装创建对象的细节，从而通过调用函数来达到复用的目的。
// 但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来，它只是简单的封装了复用代码，而没有建立起对象和类型间的关系。

// （2）第二种是构造函数模式。js 中每一个函数都可以作为构造函数，只要一个函数是通过 new 来调用的，
// 那么我们就可以把它称为构造函数。执行构造函数首先会创建一个对象，然后将对象的原型指向构造函数的 prototype 属性，然后将执行上下文中的 this 指向这个对象，最后再执行整个函数，如果返回值不是对象，则返回新建的对象。因为 this 的值指向了新建的对象，因此我们可以使用 this 给对象赋值。构造函数模式相对于工厂模式的优点是，所创建的对象和构造函数建立起了联系，因此我们可以通过原型来识别对象的类型。但是构造函数存在一个缺点就是，造成了不必要的函数对象的创建，因为在 js 中函数也是一个对象，因此如果对象属性中如果包含函数的话，那么每次我们都会新建一个函数对象，浪费了不必要的内存空间，因为函数是所有的实例都可以通用的。

// （3）第三种模式是原型模式，因为每一个函数都有一个 prototype 属性，这个属性是一个对象，
// 它包含了通过构造函数创建的所有实例都能共享的属性和方法。因此我们可以使用原型对象来添加公用属性和方法，从而实现代码的复用。这种方式相对于构造函数模式来说，解决了函数对象的复用问题。但是这种模式也存在一些问题，一个是没有办法通过传入参数来初始化值，另一个是如果存在一个引用类型如 Array 这样的值，那么所有的实例将共享一个对象，一个实例对引用类型值的改变会影响所有的实例。

// （4）第四种模式是组合使用构造函数模式和原型模式，这是创建自定义类型的最常见方式。
// 因为构造函数模式和原型模式分开使用都存在一些问题，因此我们可以组合使用这两种模式，通过构造函数来初始化对象的属性，通过原型对象来实现函数方法的复用。这种方法很好的解决了两种模式单独使用时的缺点，但是有一点不足的就是，因为使用了两种不同的模式，所以对于代码的封装性不够好。

// （5）第五种模式是动态原型模式，这一种模式将原型方法赋值的创建过程移动到了构造函数的内部，
// 通过对属性是否存在的判断，可以实现仅在第一次调用函数时对原型对象赋值一次的效果。这一种方式很好地对上面的混合模式进行了封装。

// （6）第六种模式是寄生构造函数模式，这一种模式和工厂模式的实现基本相同，我对这个模式的理解是，
// 它主要是基于一个已有的类型，在实例化时对实例化的对象进行扩展。这样既不用修改原来的构造函数，也达到了扩展对象的目的。它的一个缺点和工厂模式一样，无法实现对象的识别。

// 嗯我目前了解到的就是这么几种方式。


// V8


// map
function map (arr, mapCallback) {
  if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
    return []
  } else {
    let result = [];
    for (let i = 0; i < arr.length;i++) {
      result.push(mapCallback(arr[i], i, arr));
    }
    return result;
  }
}

// filters

function filter (arr, filterCallback) {
  if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') {
    return  []
  } else {
    let result =[]
    for (let i = 0; i < arr.length; i++) {
      if (filterCallback(arr[i], i, arr)) {
        result.push(arr[i])
      }
    }
    return result;
  }
}

// reduce
function reduce  (arr, reduceCallback, initialValue) {
  if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function') {
    return  []
  } else {
    let hasInitialValue = initialValue !== 'undefined';
    let value = hasInitialValue ? initialValue : arr[0];

    for (let i = hasInitialValue ? 1 : 0; i < arr.length; i++) {
      value = reduceCallback(value, arr[i], i, arr)
    }
    return value
  }
}

// 浅拷贝
var shallowCopy = function(obj) {
  // 只拷贝对象
  if (typeof obj !== 'object') return;
  // 根据obj的类型判断是新建一个数组还是对象
  var newObj = obj instanceof Array ? [] : {};
  // 遍历obj，并且判断是obj的属性才拷贝
  for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
          newObj[key] = obj[key];
      }
  }
  return newObj;
}

// 深拷贝
function cloneDeep (target, map = new WeakMap()) {
  if (typeof target === 'object') {
    let cloneTarget = Array.isArray(target) ? [] : {};
    if (map.get(target)) {
      return target
    }
    map.set(target, cloneTarget)

    for (const key in target) {
      cloneTarget[key] = cloneDeep(target[key], map);
    }
    return cloneTarget
  } else {
    return target
  }
}

// new 实现
function myNew () {
  var obj = {};
  var Constructor = [].shift.apply(arguments);
  obj.__proto__ = Constructor.prototype;
  var ret = Constructor.apply(obj, arguments);
  return typeof ret === 'object' ? ret : obj
}

// Promise 面试版
function myPromise () {
  let self = this;
  self.value = undefined;
  self.status = 'pending';
  self.reason = undefined;
  function resolve (value) {
    if (self.status === 'pending') {
      self.value  = value;
      self.status = 'resolved';
    }
  }

  function reject(reason) {
    if (self.value === 'pending') {
      self.reason = reason;
      self.status = 'rejected';
    }
  }

  try {
    constructor(resolve, reject)
  } catch (err) {
    reject(err)
  }
}

myPromise.prototype.then = function (onFullfilled, onRejected) {
  let self = this
  switch (self.status) {
    case 'resolved':
      onFullfilled(self.value);
      break;
    case 'rejected' :
      onRejected(self.reason);
      break;
    default:
  }
}

// Promise.all

Promise.all = function (promises) {
  let values = [];
  let count = 0;
  return new Promise((resolve) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then((res) => {
        count++;
        values[index] = res;
        if (count === promises.length) {
          resolve(values)
        }
      }, err => {
        console.log(err)
      })
    })
  })
}

// instanceof 原理
function myInstanceOf (left, right) {
  let prototype = right.prototype;
  let left = left.__proto__;
  while (true) {
    if (left === null || left === undefined) {
      return false
    }
    if (prototype === left)
    return true
    left = left.__proto__
  }
}

// 函数珂里化

function add () {
  let _args = [...arguments];
  function fn () {
    _args.push(...arguments);
    return fn
  }

  fn.toString = function () {
    return _args.reduce((sum, cur) => sum + cur)
  }
  return fn;
}

add(1)(2)(3)

// 结构
// babel 转换原理
// 浏览器的工作原理
// React Fiber 架构
// 高阶组件
//

// 函数防抖 是指在事件被触发 n 秒后再执行回调，如果在这 n 秒内事件又被触发，
// 则重新计时。这可以使用在一些点击请求的事件上，避免因为用户的多次点击向后端发
// 送多次请求。
// 函数节流 是指规定一个单位时间，在这个单位时间内，
// 只能有一次触发事件的回调函数执行，如果在同一个单位时间内某事件被触发多次
// ，只有一次能生效。节流可以使用在 scroll 函数的事件监听上，
// 通过事件节流来降低事件调用的频率。


// 节流
function throttle (fn, delay) {
  let last = 0;
  return function () {
    let context = this
    let args = arguments;
    let now = +new Date();

    if (now - last >= delay) {
      last = now;
      fn.apply(context, args);
    }
  }
}

// 用throttle来包装scroll的回调
// const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)


// fangdou

function debounce(fn, delay) {
  let timer = null;
  return function () {
    let context = this;
    let args = arguments
    if (timer) clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}

// 优化

function throttle (fn, delay) {
  let last = 0, timer = null;
  return function () {
    let context = this;
    let args = arguments;
    let now = +new Date();

    if (now - last < delay) {
      clearTimeout(timer);
      timer = setTimeout(function () {
        last = now
        fn.apply(context, args)
      }, delay)
    } else {
      last = now;
      fn.apply(context, args)
    }
  }
}

// es5 数组去重

function unique (arr) {
  if (!Array.isArray(arr)) {
    console.log(err, 'type')
  }
  var array = [];
  for (let i = 0; i < arr.length; i++) {
    if (array.indexOf(arr[i]) === -1) {
      arr.push(arr[i])
    }
  }
  return array
}


// 冒泡排序
function bubbleSort (arr) {
  for (let i = 0; i < arr.length; i++) {
    let flag = true;
    for (let j = 0; i < arr.length - i - 1; j++) {
      if (arr[i] > arr[j + 1]) {
        flag = false
        let temp = arr[j];
        temp[j] = arr[j + 1];
        arr[j + 1] = temp
      }
    }
    if (flag) break;
  }
  return arr
}

// instanceof

function myInstanceOf (left, right) {
  let proto = Object.getPrototypeOf(left);

  while (true) {
    if (proto === null) return false;
    if (proto === right.prototype) return true;

    proto = Object.getPrototypeOf(proto)
  }
}

// 实现红绿灯

function red () {
  console.log('red')
}

function green () {
  console.log('green')
}

function yellow () {
  console.log('yellow')
}

let light = function (timer, cb) {
  return new Promise((resolve) => {
    setTimeout(() => {
      cb()
      resove()
    }, timer)
  }) 
}

const step = function () {
  Promise.resolve().then(() => {
    return light(3000, red)
  }).then(() => {
    return light(2000, green)
  }).then(() => {
    return light(1000, yellow)
  }).then(() => {
    return step()
  })
}

// 图片懒架加载

let img = document.getElementsByTagName('img');
let num = img.length;
let count = 0;

function lazyload () {
  for (let i = count; i < num; i++) {
    if (img[i].getBoundingClientRect().top < document.documentElement.clientHeight) {
      if (img[i].getAttribute('src') !== 'default') continue;
      img[i].src = img[i].getAttribute('data-src');
      count++
    }
  }
}

window.addEventListener('scroll', throttle(lazyload, 200))

// call

Function.prototype.myCall = function (context) {
  if (typeof this !== 'function') {
    console.log('err');
  }
  let args = [...arguments].slice(0),
      result = null,
      context = context || window;
      context.fn = this;
      
  result = context.fn(...args);
  delete context.fn
  return result
}

// apply

Function.prototype.myApply = function (context) {
  if (typeof this !== 'function') {
    console.log('err');
  }

  let result = null;
  context = context || window;
  context.fn = this;

  if (arguments[1]) {
    result = context.fn(...arguments[1]);
  } else {
    context = context.fn()
  }
  delete context.fn
  return result
}

// Redux

function createState () {
  let state;
  let listeners = [];

  function getState () {
    return state;
  }

  function dispatch (action) {
    state = reducer(state, action)
    listeners.forEach(l => l())
  }

  function subscribe (listener) {
    listeners.push(listener);
    return function () {
      const index = listeners.indexOf(listener);
      listeners.splice(index, 1)
    }
  }
  dispatch({})

  return {
    getState,
    dispatch,
    subscribe
  }
}

// webpack plugins

class DemoPlugin {
  constructor(options) {
    this.options = options
  }
  apply (compiler) {
    console.log(compiler)
  }
}

new DemoPlugin({
  name: 'xxx'
})


class myPlugin {
  constructor (options) {

  }
  apply (compiler) {
    compiler.hook.emit.tap('myPlugin', compilation => {
      for (const name in compilation.assets) {
        if (name.endsWith('.js')) {
          const contents = compilation.assets[name].source()
        }
      }
    })
  }
}

// 

function getUrlQueryStringAll () {
  let searchStr = window.location.search;
  let search = searchStr.substr(1, searchStr.length);
  let res = {}
  if (!search) return

  search.split('&').map(item => {
    const [key, value] = item.split('=');
    res[key] = decodeURIComponent(value)
  })
  return res
}

// 

class Observer {
  constructor(fn) {
    this.update = fn
  }
}

class Subject {
  constructor() {
    this.observers = []
  }

  addObserver (observer) {
    this.observers.push(observer)
  }

  notify () {
    this.observers.forEach(observer => {
      observer.update()
    })
  }
}

var subject = new Subject();

const update = () => {console.log('22')}

var obj1 = new Observer(update)

// 344

function  fomartMobile(value) {
  let strMobile  = value.replace(/\+s/g, '');
  return `${strMobile.substring(0, 3)} ${strMobile.substring(3, 7)} ${strMobile.substring(7, 11)}`
}
fomartMobile('15021983065')


// Promise.all

Promise.all = function (promises) {
  let values = [];
  count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then((res) => {
        count++;
        values[index] = res
        if (count === promises.length) {
          resolve(values)
        }
      })
    }) 
  })
}